package org.freeplane.features.export.mindmapmode;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilePermission;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.security.Permission;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.util.List;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.freeplane.core.ui.components.UITools;
import org.freeplane.core.util.LogUtils;
import org.freeplane.core.util.TextUtils;
import org.freeplane.features.map.MapWriter.Mode;
import org.freeplane.features.map.NodeModel;

public class XsltExportEngine implements IExportEngine {
	
	static class XsltExportPolicy extends Policy {
		static FilePermission allFileReadPermission = new FilePermission( "<<ALL FILES>>" , "read,readlink");
		final Policy alternativePolicy;
		public XsltExportPolicy() {
			this(Policy.getPolicy());
		}
		public XsltExportPolicy(Policy alternativePolicy) {
			super();
			this.alternativePolicy = alternativePolicy;
		}
		@Override
		public boolean implies(ProtectionDomain domain, Permission permission) {
			return alternativePolicy.implies(domain, permission) || allFileReadPermission.implies(permission);
		}
		
		 public void remove(){
			 if(Policy.getPolicy() != this)
				 throw new IllegalStateException();
			Policy.setPolicy(alternativePolicy);
		}
		
	}
	public XsltExportEngine(File xsltFile) {
	    super();
	    this.xsltFile = xsltFile;
    }

	// generated by eclipse but modified to include only file _names_
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((xsltFile == null) ? 0 : xsltFile.getName().hashCode());
		return result;
	}
	
	// generated by eclipse but modified to compare only file _names_
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		XsltExportEngine other = (XsltExportEngine) obj;
		if (xsltFile == null) {
			if (other.xsltFile != null)
				return false;
		} else if (!xsltFile.getName().equals(other.xsltFile.getName()))
			return false;
		return true;
	}
	
	final private File xsltFile;
	
	public void export(List<NodeModel> branches, File toFile) {
		final Source xsltSource = new StreamSource(xsltFile);
		final Source xmlSource = getMapXml(branches);
		final XsltExportPolicy xsltExportPolicy = new XsltExportPolicy();
		Policy.setPolicy(xsltExportPolicy);
        try (OutputStream outputStream = new FileOutputStream(toFile)){
        	final Result result = new StreamResult(outputStream);
        	final TransformerFactory transFact = TransformerFactory.newInstance();
        	final Transformer trans = transFact.newTransformer(xsltSource);
        	trans.transform(xmlSource, result);
        }
        catch (final Exception e) {
        	UITools.errorMessage(TextUtils.getText("export_failed"));
        	LogUtils.warn(e);
        }
        finally {
        	xsltExportPolicy.remove();
        }
	}

	private StreamSource getMapXml(final List<NodeModel> branches) {
		final StringWriter writer = new StringWriter();
		new BranchXmlWriter(branches).writeXml(writer, Mode.EXPORT);
		final StringReader stringReader = new StringReader(writer.getBuffer().toString());
		return new StreamSource(stringReader);
	}
}
